package com.hohai.river.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hohai.river.entity.River;
import com.hohai.river.entity.Score;
import com.hohai.river.entity.SysIndex;
import com.hohai.river.mapper.SysIndexMapper;
import com.hohai.river.service.RiverService;
import com.hohai.river.service.ScoreService;
import com.hohai.river.service.SysIndexService;
import com.hohai.river.utils.JsonUtils;
import com.pig4cloud.pig.admin.api.feign.RemoteFileService;
import com.pig4cloud.pig.common.core.exception.ErrorCodes;
import com.pig4cloud.pig.common.core.util.MsgUtils;
import com.pig4cloud.pig.common.security.service.PigUser;
import com.pig4cloud.pig.common.security.util.SecurityUtils;
import feign.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 指标表
 *
 */
@Service
public class SysIndexServiceImpl extends ServiceImpl<SysIndexMapper, SysIndex> implements SysIndexService {

	@Autowired
	private ScoreService scoreService;

	@Autowired
	private RiverService riverService;

	@Resource
	RemoteFileService remoteFileService;

	@Override
	public Boolean removeIndexById(Long id) {
		// 查询父节点为当前节点的节点
		List<SysIndex> indexList = this.list(Wrappers.<SysIndex>query().lambda().eq(SysIndex::getPid, id));

		Assert.isTrue(CollUtil.isEmpty(indexList), MsgUtils.getMessage(ErrorCodes.SYS_MENU_DELETE_EXISTING));

		this.baseMapper.delete(Wrappers.<SysIndex>query().lambda().eq(SysIndex::getId, id));
		// 删除当前菜单及其子菜单
		return this.removeById(id);
	}

	@Override
	public List<Tree<Long>> treeIndexFiltered(Long riverId, Boolean expert) {
		PigUser user = SecurityUtils.getUser();
		River river = riverService.getById(riverId);
		List<Score> indexFinishedIds = null;
		if (expert) {
			indexFinishedIds = scoreService.getIndexFinishedExpert(riverId, user.getId().toString());
		}
		else {
			indexFinishedIds = scoreService.getIndexFinishedNormal(riverId);
		}

		Response response = remoteFileService.file(JsonUtils.BUCKET, JsonUtils.FILE_NAME);
		JSONObject jsonObject = JsonUtils.getJsonObjectFromResponse(response);

		List<TreeNode<Long>> collect = this.baseMapper
				.selectList(
						Wrappers.<SysIndex>lambdaQuery()
								.select(SysIndex::getId, SysIndex::getPid, SysIndex::getIndexName,
										SysIndex::getSortOrder, SysIndex::getIndexCode)
								.orderByAsc(SysIndex::getSortOrder))
				.stream().filter(item -> {
					if (item.getPid().equals(-1L))
						return true;
					JSONObject options = jsonObject.getJSONObject(item.getIndexCode() + "Index");
					if (options == null)
						return false;
					return ((Integer) options.getJSONArray(river.getLevel().toString()).get(0)) > 0;
				}).map(getNodeFilteredFunction(indexFinishedIds, expert, user.getId().toString()))
				.collect(Collectors.toList());
		List<Tree<Long>> treeList = TreeUtil.build(collect, -1L);
		for (Tree<Long> tree : treeList) {
			List<Tree<Long>> children = tree.getChildren();
			if (children == null) {
				continue;
			}
			BigDecimal score = new BigDecimal("0.0");
			boolean finished = true;
			for (Tree<Long> child : children) {
				if (!((Boolean) child.get("finished"))) {
					finished = false;
					break;
				}
				score = score.add(new BigDecimal((child.get("score").toString())));
			}
			tree.putExtra("finished", finished);
			if (finished) {
				tree.putExtra("score", score.doubleValue());
			}
		}
		return treeList;
	}

	@Override
	public List<Tree<Long>> treeIndex() {
		List<TreeNode<Long>> collect = this.baseMapper
				.selectList(Wrappers.<SysIndex>lambdaQuery().orderByAsc(SysIndex::getSortOrder)).stream()
				.map(getNodeFunction()).collect(Collectors.toList());

		return TreeUtil.build(collect, -1L);
	}

	@NotNull
	private Function<SysIndex, TreeNode<Long>> getNodeFunction() {
		return index -> {
			TreeNode<Long> node = new TreeNode<>();
			node.setId(index.getId());
			node.setName(index.getIndexName());
			node.setParentId(index.getPid());
			node.setWeight(index.getSortOrder());
			// 扩展属性
			Map<String, Object> extra = new HashMap<>();
			extra.put("indexCode", index.getIndexCode());
			extra.put("sortOrder", index.getSortOrder());
			extra.put("indexDescription", index.getIndexDescription());
			extra.put("supportMaterial", index.getSupportMaterial());
			node.setExtra(extra);
			return node;
		};
	}

	@NotNull
	private Function<SysIndex, TreeNode<Long>> getNodeFilteredFunction(List<Score> indexFinishedIds, boolean isExpert,
			String userId) {
		return index -> {
			TreeNode<Long> node = new TreeNode<>();
			node.setId(index.getId());
			node.setName(index.getIndexName());
			node.setParentId(index.getPid());
			node.setWeight(index.getSortOrder());
			// 扩展属性
			Map<String, Object> extra = new HashMap<>();
			extra.put("indexCode", index.getIndexCode());
			extra.put("pid", index.getPid());
			boolean finished = false;
			for (Score indexFinished : indexFinishedIds) {
				if (indexFinished.getIndexId().equals(index.getId())) {
					finished = true;
					if (isExpert) {
						extra.put("score", JSON.parseObject(indexFinished.getExpertList()).getDouble(userId));
					}
					else {
						extra.put("score", indexFinished.getSelfScore());
					}
					break;
				}
			}
			extra.put("finished", finished);
			node.setExtra(extra);
			return node;
		};
	}

	@Override
	public SysIndex getInfoByIndexCode(String indexCode) {
		return this.baseMapper.selectOne(
				Wrappers.<SysIndex>lambdaQuery().select(SysIndex::getIndexDescription, SysIndex::getSupportMaterial)
						.eq(SysIndex::getIndexCode, indexCode));
	}

}
